//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <mantle.h>
#include <util.h>

EXTERN_C ECode CDECL SysExitThread(ECode exitCode);

PInterface InProcessLinkNode::GetObject()
{
    return (IThread*)THIS(this, Thread, m_inProcLink);
}

// Interface functions
//
PInterface Thread::Probe(
    /* [in] */ REIID riid)
{
    if (riid == EIID_IInterface) {
        return (IInterface *)(IThread *)this;
    }
    else if (riid == EIID_IThread) {
        return (IThread *)this;
    }
    else if (riid == EIID_IThreadContext) {
        return (IThreadContext *) (_CThreadContext *)this;
    }
    else if (riid == EIID_CLASS_INFO) {
        return (PInterface)&ECLSID_CThread;
    }
    else if (riid == EIID_INTERFACE_INFO) {
        return (PInterface)&EIID_IThread;
    }

    return NULL;
}

PInterface Thread::ObjProbe(
    REIID Self_riid,
    REIID riid)
{
    if (riid == EIID_IInterface) {
        return (PInterface)(IThread*)(_CThread *)this;
    }
    else if (riid == EIID_IThread) {
        return (IThread *)(_CThread *)this;
    }
    else if (riid == EIID_IThreadContext) {
        return (IThreadContext *)(_CThreadContext *)this;
    }
    else if (riid == EIID_CLASS_INFO) {
        return (PInterface)&ECLSID_CThread;
    }
    else if (riid == EIID_INTERFACE_INFO) {
        return (PInterface) &Self_riid;
    }

    return NULL;
}

UInt32 Thread::AddRef(void)
{
    return m_inProcLink.m_cRef.Increment();
}

UInt32 Thread::Release(void)
{
    Int32 lRef = m_inProcLink.m_cRef.Decrement();

    assert(lRef >= 0);

    if (ThreadState_Running != m_uPoolThreadState) {
        if (1 == lRef && !this->IsFinished() && m_pOwner) {
            m_pOwner->m_threadPool.PutBackThread(this);
        }
        else if (0 == lRef) {
            if (m_pOwner) {
                m_pOwner->m_threadLock.Lock();
                m_pOwner->m_threadPool.m_threadLock.Lock();
                m_inProcLink.Detach();
                m_pOwner->m_threadPool.m_threadLock.Unlock();
                m_pOwner->m_threadLock.Unlock();
            }
            delete this;
        }
    }
    else {
        assert(0 != lRef);
    }
    return (UInt32)lRef;
}

ECode Thread::Aggregate(
    /* [in] */ AggregateType type,
    /* [in] */ IInterface* pObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode Thread::GetDomain(
    /* [out] */ IInterface** ppObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode Thread::Start()
{
    if (NULL == m_pOwner) {
        return NOERROR;
    }

    if (ThreadState_Unstarted == m_uPoolThreadState) {
        DzGetSystemTime(&m_startTime);
    }

    if (ThreadState_Unstarted \
        != InterlockedCompareExchange((Int32*)&m_uPoolThreadState,
                ThreadState_Running, ThreadState_Unstarted)) {
        return NOERROR;
    }

    m_pOwner->m_threadLock.Lock();
    m_exitCode = E_THREAD_ABORTED;
    if (ProcessState_Finished == m_pOwner->m_processState &&
        m_pOwner != ::GetCurrentProcess()) {
        m_pOwner->m_threadLock.Unlock();
        return E_PROCESS_ALREADY_EXITED;
    }

    m_pOwner->AddRef();

    m_pOwner->m_threadList.InsertLast(&m_inProcLink);
    m_pOwner->m_nThreadListState++;
    this->AddRef();
    m_pOwner->m_threadLock.Unlock();

    m_joinEvent.Clear();
    m_startEvent.Notify();
    return NOERROR;
}

ECode Thread::Join(
    /* [in] */ Millisecond32 timeout,
    /* [out */ WaitResult *pResult)
{
    if (this == ::GetCurrentThread()) return E_INVALID_OPERATION;

    WaitResult wr;
    if (NULL == pResult) pResult = &wr;

    if (INFINITE == timeout) {
        m_joinEvent.Wait(pResult, NULL);
    }
    else {
        m_joinEvent.TryWait(DzMillisecondsToTicks(timeout), pResult, NULL);
    }
    return NOERROR;
}

ECode Thread::Abort()
{
    uint_t uPoolThreadState;

    if (this == ::GetCurrentThread()) {
        SysExitThread(NOERROR);
        return NOERROR; // Return to another user
                        // thread entry.(From thread pool)
    }

    if (ThreadState_Finished != m_uPoolThreadState) {
        DzGetSystemTime(&m_finishTime);
    }

    uPoolThreadState = InterlockedExchange(
        (Int32*)&m_uPoolThreadState, ThreadState_Finished);
    if (ThreadState_Finished == uPoolThreadState) {
        return NOERROR;
    }

    TryToSendReceiveDebugMsg(
        DEBUGGEE_THREAD_EXIT, (UInt32)this->m_exitCode, m_pOwner, this);

    if (m_pOwner) {
        if (ThreadState_Unstarted == uPoolThreadState) {
            m_pOwner->AddRef();
        }
        else {
            assert(ThreadState_Running == uPoolThreadState);
            m_pOwner->m_threadLock.Lock();
            m_inProcLink.Detach();
            m_inProcLink.Initialize(); // for detach twice.(In thread release)
            m_pOwner->m_nThreadListState++;
            m_pOwner->m_threadLock.Unlock();
            this->Release();
        }

        m_fnKernelEntry = NULL;
        m_bHasUserEntry = FALSE;

        if (this == m_pOwner->m_pMainThread) m_pOwner->Kill();
    }

    return Abort(m_pOwner);
}

ECode Thread::Quit()
{
    //need to be implemented
    return E_NOT_IMPLEMENTED;	
}

ECode Thread::GetId(
    /* [out] */ ThreadId * pTid)
{
    if (NULL == pTid) return E_INVALID_ARGUMENT;

    *pTid = (int)this & 0x7FFFFFFF;
    return NOERROR;
}

ECode Thread::SetName(
    /* [in] */ WString name)
{
    m_lock.Lock();

    if (name.IsNullOrEmpty()) {
        *m_wszName = 0;
    }
    else {
        wcsncpy(m_wszName, name, MAXIMUM_THREAD_NAME_LENGTH);
        m_wszName[MAXIMUM_THREAD_NAME_LENGTH - 1] = 0;
    }

    m_lock.Unlock();

    return NOERROR;
}

ECode Thread::GetName(
    /* [out] */ WStringBuf * pName)
{
    if (pName == NULL ||
        pName->GetCapacity() < MAXIMUM_THREAD_NAME_LENGTH - 1) {
        return E_INVALID_ARGUMENT;
    }

    m_lock.Lock();
    pName->Copy(m_wszName);
    m_lock.Unlock();

    return NOERROR;
}

ECode Thread::GetState(
    /* [out] */ ThreadState * pState)
{
    if (NULL == pState) return E_INVALID_ARGUMENT;

    if (ThreadState_Running != m_uPoolThreadState) {
        *pState = (ThreadState)m_uPoolThreadState;
        return NOERROR;
    }
    // consider ThreadState_InterruptibleSleeping as ThreadState_Sleeping
    *pState = (ThreadState)(m_uState & ~0x02);
    return NOERROR;
}

ECode Thread::GetQuitCode(
    /* [out] */ QuitCode * pQuitCode)
{
    if (NULL == pQuitCode) return E_INVALID_ARGUMENT;

    if (ThreadState_Finished != m_uPoolThreadState) {
        return E_THREAD_NOT_STOPPED;
    }

    *pQuitCode = m_exitCode;
    return NOERROR;
}

ECode Thread::GetStartTime(
    /* [out] */ SystemTime * pTime)
{
    if (NULL == pTime) return E_INVALID_ARGUMENT;

    if (ThreadState_Unstarted == m_uPoolThreadState) {
        return E_THREAD_UNSTARTED;
    }

    *pTime = m_startTime;
    return NOERROR;
}

ECode Thread::GetQuitTime(
    /* [out] */ SystemTime * pTime)
{
    if (NULL == pTime) return E_INVALID_ARGUMENT;

    if (ThreadState_Finished != m_uPoolThreadState
        && ThreadState_Finished != m_uState) {
        return E_THREAD_NOT_STOPPED;
    }

    *pTime = m_finishTime;
    return NOERROR;
}

ECode Thread::GetProcessorTimes(
    /* [out] */ Int32 * pUserTime,
    /* [out] */ Int32 * pKernelTime)
{
    if (NULL == pUserTime
            && NULL == pKernelTime)
        return E_INVALID_ARGUMENT;

    if (NULL != pUserTime) {
        *pUserTime = DzTicksToMilliseconds(m_uUserProcessorTicks);
    }

    if (NULL != pKernelTime) {
        *pKernelTime = DzTicksToMilliseconds(m_uPrivilegedProcessorTicks);
    }

    return NOERROR;
}

// member functions
//
void * Thread::ReallocBuffer(size_t size)
{
    // TODO: temp resolution ,we should rebuild function GET_UP_ORDER
    // and resolve the problem of big size

    if (m_pBuffer) {
        if (m_uBufferSize <= PAGE_SIZE - 4) {
            free(m_pBuffer);
        }
        else {
            DzFreeKernelPages(m_pBuffer, \
                GET_UP_ORDER(AlignPageUp(m_uBufferSize) / PAGE_SIZE));
        }
    }

    if (size == 0) {
        m_pBuffer = NULL;
        m_uBufferSize = 0;
        return m_pBuffer;
    }

    if (size <= PAGE_SIZE - 4) {
        m_pBuffer = malloc(size);
        m_uBufferSize = m_pBuffer ? size : 0;
    }
    else {
        int nUp2Order2;

        nUp2Order2 = GET_UP_ORDER(AlignPageUp(size) / PAGE_SIZE);
        m_pBuffer = DzAllocKernelPages(nUp2Order2);
        m_uBufferSize = m_pBuffer ? 1 << (PAGE_SHIFT + nUp2Order2) : 0;
    }

    return m_pBuffer;
}

//
// Can only be invoked when current thread is 'this'.
//
void Thread::ReadyToStart()
{
    uint_t  uPoolThreadState;

    assert(GetCurrentThread() == this);

    while (TRUE) {
        m_startEvent.UninterruptibleWait(NULL);

        if (NULL == m_fnKernelEntry) {
            assert(m_bHasUserEntry);
            return; // return to user mode
        }
        else {
            m_exitCode = (*m_fnKernelEntry)(m_pvKernelEntryArg);
            m_fnKernelEntry = NULL;
            if (FALSE == m_bHasUserEntry) {
                if (ThreadState_Finished != m_uPoolThreadState) {
                    DzGetSystemTime(&m_finishTime);
                }

                uPoolThreadState = InterlockedExchange(
                    (Int32*)&m_uPoolThreadState, ThreadState_Finished);
                if (ThreadState_Finished == uPoolThreadState) {
                    continue;
                }

                assert(ThreadState_Running == uPoolThreadState);
                m_pOwner->m_threadLock.Lock();
                m_inProcLink.Detach();
                // for detach twice.(In thread release)
                m_inProcLink.Initialize();
                m_pOwner->m_nThreadListState++;
                m_pOwner->m_threadLock.Unlock();

                m_joinEvent.Notify();

                // If thread's reference equal 1,
                // put back pCurThd to thread pool.
                // If the thread pool has overloaded, pCurThd will be destroy.
                this->Release();
            }
            else {
                m_exitCode = E_THREAD_ABORTED;  // Reset the default
                                                // finish code
                return; // return to user mode
            }
        }
    }
}
